#include "lib/print.h"
#include "lib/str.h"
#include "lib/lock.h"
#include "mem/pmem.h"
#include "mem/vmem.h"
#include "mem/mmap.h"
#include "memlayout.h"

// 包装 mmap_region_t 用于仓库组织
typedef struct mmap_region_node {
    mmap_region_t mmap;
    struct mmap_region_node* next;
} mmap_region_node_t;

#define N_MMAP 256

// mmap_region_node_t 仓库(单向链表) + 指向链表头节点的指针 + 保护仓库的锁
static mmap_region_node_t list_mmap_region_node[N_MMAP];
static mmap_region_node_t* list_head;
static spinlock_t list_lk;

// 初始化上述三个数据结构
void mmap_init()
{
    // 初始化锁
    spinlock_init(&list_lk, "mmap");

    for (int i = 0; i < N_MMAP - 1; i++)
    {
        list_mmap_region_node[i].mmap.begin = MMAP_BEGIN;
        list_mmap_region_node[i].mmap.npages = 0;
        list_mmap_region_node[i].next = &list_mmap_region_node[i + 1];
    }
    list_mmap_region_node[N_MMAP - 1].mmap.begin = MMAP_BEGIN;
    list_mmap_region_node[N_MMAP - 1].mmap.npages = 0;
    list_head = list_mmap_region_node;
}

// 从仓库申请一个 mmap_region_t
// 若申请失败则 panic
// 注意: list_head 保留, 不会被申请出去
mmap_region_t* mmap_region_alloc(bool init)
{
    // 获取仓库的锁，确保原子操作
    spinlock_acquire(&list_lk);

    // 如果仓库为空，触发 panic
    mmap_region_node_t* node = list_head->next;
    if (node == NULL) {
        panic("mmap_region_alloc: No mmap region available in the warehouse.");
    }

    // 更新链表头指针，指向下一个节点
    list_head->next = node->next;

    // 释放锁，确保操作完成
    spinlock_release(&list_lk);

    // 如果 init == true，初始化 mmap_region
    if (init) {
        node->mmap.begin = MMAP_BEGIN;
        node->mmap.npages = (MMAP_END - MMAP_BEGIN) / PGSIZE;
        node->mmap.next = NULL;
    }

    // 返回申请到的 mmap_region_t
    return &node->mmap;
}


// 向仓库归还一个 mmap_region_t
void mmap_region_free(mmap_region_t* mmap)
{
    spinlock_acquire(&list_lk);  // 获取仓库的锁
    
    // 计算归还的 mmap_region_t 对应的节点在数组中的索引
    int id = ((uint64)mmap - (uint64)&list_mmap_region_node->mmap) / sizeof(list_mmap_region_node[0]);
    
    // 将归还的节点插入到仓库的头部
    list_mmap_region_node[id].next = list_head->next;
    list_head->next = &list_mmap_region_node[id];
    
    spinlock_release(&list_lk);  // 释放锁
}

// 输出仓库里可用的 mmap_region_node_t
// for debug
void mmap_show_mmaplist()
{
    spinlock_acquire(&list_lk);
    
    mmap_region_node_t* tmp = list_head;
    int node = 1, index = 0;
    while (tmp)
    {
        index = tmp - list_head;
        printf("node %d index = %d\n", node++, index);
        tmp = tmp->next;
    }

    spinlock_release(&list_lk);
}